        .globl start
start:
	// start in a 16-bit mode
	.code16
	// clear interrupt flag;
	// if = 0;
	// all possible interrupts are blocked;
	// Why? For the following A20 gate (address line).
	cli
	// clear direction flag;
	// df = 0;
	// string operations increment
	cld		

	xorw	%ax, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %ss

	/* Why this code?
	* There are 20 (numbered 0--19) address lines in 8086/8088, so
	* the addressing space is 2^20=1M. However, the 8086/8088 makes
	* use of 16-bit addressing mode, which means the addressing space
	* is only 2^16=64K. To resolve this, Intel introduced the
	* segmented addressing mode:
	*       address = baseAddress * 16 + offset
	* 
	* However, one odd thing is that the maximum address is
	*	0xffff0+0xffff = 0x10ffef
	*		       = 1M + 64K - 16bytes
	* So, when the given address>1M, the system silently
	* wrap-around to zero.
	*
	* As to 80286, the number of system address lines grow to 24, and
	* the addressing space is 2^24 = 16M. However, the problem is how
	* to treat the space between 1M---1M+64K-64bytes?
	* For back compatiability, IBM uses some flag on keybroad controller
	* to controll A20 (the 21th address line), which called A20 gate (the
	* address line).
	* Once A20 is enabled, the space is real, and when A20 gate is disabled,
	* the space is wrap-around to keep back-compatiable.
	*
	* And put all these together, we know that once the A20 gate
	* is disabled, the addressing space is:
	* 	0x000000---0x0fffff
	*	0x200000---0x2fffff
	* 	0x400000---0x4fffff
	* 	etc.
	* and once A20 gate is enabled, the addressing space is continuous.
	*
	* Then how to enable A20 gate?
	* The details will be explained int the keyboard control, and the
	* short answer is: by the keyboard controller 8042. By setting
	* the output port (64h)'s second bit. But however, we first
	* handle the input data in keyboard buffer.
	*/		
seta20.1:
	// read a byte from port 0x64 into %al
	inb	$0x64, %al
	// bit-wise logical and.
	// bit #1 denotes wether the input buffer is empty
	testb	$0x2, %al
	// loop, if not zero
	jnz	seta20.1

	// 0xd1: we want to write output port: 0x60
	movb	$0xd1, %al
	outb	%al, $0x64

seta20.2:
	inb	$0x64, %al
	testb	$0x2, %al
	jnz	seta20.2

	// 1101,1111 ===> port 0x60???
	// Why?
	movb	$0xdf, %al
	outb	%al, $0x60

	// A simple way to enable A20 gate (called fast A20):
	// movw	$0x2401, %ax
	// int	$0x15


	
	/* Real Mode to Protected Mode Switching
	* 
	* First, let's recall the programming model of real mode. In
	* this mode, the address is calculated via:
	*	address = baseAddress*16 + offset
	* where both baseAddress and offset is 16-bit long.
	*
	* In protected mode, there are two basic models: segment-based
	* and page-based. Let's consider the first now. On IA32, the
	* address is 32-bit, which means that the baseAddress and
	* offset is both 32-bit. And IA32 specifies that the limit of
	* a segment is n*4K for some n. And the segment itself should
	* specifies some kind of protection mode, so under protection
	* mode, one segment looks like a triple:
	* 	[baseAddress, limit, Access].
        *
	* And this triple is cooked into a data structure called
	* "segment desciptor" (64-bit long). So we must put such a
	* data structure into a register, if we want to refer to
	* some segment.
	* The sad thing is that Intel keep the segment register 16-bit
	* to keep back-compability, so we can NOT put segment
	* descriptor into a single segment register. :-(
	*
	* How to resolve this? Pointers!
	* All segment descriptor are stored into a globl array called GDT, and
	* the segment register serves as an array index.
        *
        * And one final problem is that the machine should know where to
        * find the GDT?
        * So Intel designs a designated register GDTR to store the base
	* address of GDT, and the programmer could load that address into
	* GDTR register via the LGDT instruction. After that, the machine
	* is able to find the GDT. :-)
	* 
	* Now let's take a close look at the GDT index---the segment selector
	* (the data structure stored in a segment register).
	*	bit3-bit15: the index
	*	bit2: gdt or ldt
	* 	bit1 and bit0: not care for now
	*
	* The addressing process:
	* 1. Load the segment selector into a segment register
	*	2. Indexing into the GDT
	*	3. Get the baseAddress
	*	4. address = baseAddress + offset
	* Note that both baseAddress and offset are 32-bit.
	*
	* 
	*/
	lgdt	gdtDescription
	
        // some other debug code
        // What I'd like to do is to call an externel
        // function, but it seems does not work...
        /*
        movw    $start, %sp
        pushw   $debugStr2
        call    myPrintfDebug
        */
        /*
        movw    $debugStr2, %si
loop2:
        lodsb
        andb    %al, %al
        jz      end2
        movb    $0x0e, %ah
        movw    $7, %bx
        sti
        int     $0x10
        cli
        jmp     loop2
end2:   
*/
        // set the 0-bit of %cr0 with 1 (protected mode enabled)
	movl	%cr0, %eax
	orl	$1, %eax
	movl	%eax, %cr0

        // GDT selector : offset
	ljmp	$0x08, $protCSeg

	.code32
protCSeg:
	movw	$0x10, %ax
	movw	%ax, %ds
	movw	%ax, %es
	movw	%ax, %fs
	movw	%ax, %gs
	movw	%ax, %ss

        movw    $0x18, %ax
        movw    %ax, %gs
        
	movl	$start, %esp
	call	bootMain

shouldNotToHere:
	jmp	shouldNotToHere

// 2^2 bytes alignment
.p2align 2
gdt:
	// The first descriptor:
        // System requires that the first entry to be emtpy
	.word 0, 0, 0, 0
        
	// The second: a code segment
	// size = 4G
	.word 0xffff
	// base address
	.word 0x0000
	// access
	.word 0x9a00
	// limit
	.word 0x00cf
	
	// a data segment
	.word 0xffff
	.word 0x0000
	.word 0x9200
	.word 0x00cf

        // a display segment
        .word 0xffff
        .word 0x8000
        .word 0x920b
        .word 0x00cf

        // Not the GDT base address.
gdtDescription:
	// sizeof (gdt)-1
	.word	0x1f
	// address gdt
	.long	gdt

        
        